/******************************************************************************
 * Copyright (C) 2025 EternalChip, Inc.(Gmbh) or its affiliates.
 * 
 * All Rights Reserved.
 * 
 * @file ec_bsp_aht21_driver.c
 * 
 * @par dependencies 
 * - ec_bsp_aht21_driver.h
 * 
 * @author H
 * 
 * @brief Implete the HAL operations of AHT21 and corresponding opetions.
 * 
 * Processing flow:
 * 
 * call directly.
 * 
 * @version V1.0
 * 
 * @note 1 tab == 4 spaces!
 * 
 *****************************************************************************/
 
 //******************************** Includes *********************************//
#include "ec_bsp_aht21_driver.h"
#include "ec_bsp_aht21_reg.h"
#include "elog.h"
//******************************** Includes *********************************//

//******************************** Defines **********************************//
#define DEBUG

#define AHT21_MEASURE_WAITING_TIME	80					//	Measurement time [ms]
#define AHT21_NOT_INITED						0						//	Not init. flag
#define AHT21_INITED								1						//	Init. flag
#define AHT21_ID										0x18				//	AHT21 ID

#define CRC8_POLYNOMIAL							0x31				//	CRC-8 polynomial
#define CRC8_INITIAL								0xFF				//	CRC-8 initial value

#define IS_INITED		(AHT21_INITED == g_inited)	// if the device has been inited

//******************************** Defines **********************************//

//******************************** Variables ********************************//
static int8_t g_inited				= 	AHT21_NOT_INITED;		//Init. flag
static uint8_t g_device_id		=									 0;		//Device_id

//******************************** Variables ********************************//

/**
 * @brief  Function for calculating CRC-8 checksum.
 *
 * Steps:
 *	1,XOR each byte of the input data with the CRC register.
 *	2,for each bit, check the most significant bit.
 *	3,Shift the CRC register left and, if the most significant bit is 1,
 *	XOR with the polynomial.
 *
 * @param[in] p_data	:	Pointer to the input data.
 * @param[in] length	:	Length of the input data.
 *
 * @return uint8_t	:	The calculated CRC-8 checksum.
 *
 */
static uint8_t CheckCrc8(const uint8_t *p_data,const uint8_t length)
{
		uint8_t crc = CRC8_INITIAL;	//	Initialize CRC register
	
		for(uint8_t i = 0; i < length; i++)
		{
				crc ^= p_data[i];	//XOR the current byte with the CRC
			
				if (crc & 0x80)
				{
						// if MSB is 1, shift and XOR with polynomial
						crc = (crc << 1) ^ CRC8_POLYNOMIAL;
				}
				else
				{
						// Othrwise, just shift left
						crc <<= 1;
				}
		}
		
		return crc;	//	Return the final CRC value
}

/**
 * @brief  Function for rading CRC-8 ID.
 *
 * Steps:
 *	1,Figure out which objects.
 *	2,Internally call the related functions to read ID.
 *
 * @param[in] p_aht21_instance	:	The pointer to objct of bsp_aht21_driver_t.
 *
 * @return aht21_status_t.
 *
 */
static aht21_status_t __read_id(bsp_aht21_driver_t * const p_aht21_instance)
{
		uint8_t data = 0;
		
		//	Enter Critical Segmnet
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_enter(NULL);
#endif /*End of HARDWARE_IIC*/
		
		// Send the IIC start Signal
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_start(NULL);
	
		// Send command to read ID
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_bytes(NULL,\
																										AHT21_REG_READ_ADDR);
		
		// Wait the ACK of IIC slave device
		if ( AHT21_OK == \
				p_aht21_instance -> p_iic_driver_instance -> pf_iic_wait_ack(NULL))
		{
				// Receive the data from slave device
				p_aht21_instance -> p_iic_driver_instance -> pf_iic_receive_bytes(NULL,\
																																					&data);
		}
		
		// Send the stop signal
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_stop(NULL);
		
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_exit(NULL);
#endif /* End of HARDWARE_IIC*/
		
		// Check the ID
		if ( AHT21_ID == (data & AHT21_ID) )
		{
				g_device_id = AHT21_ID;
			
				return AHT21_OK;
		}
		
		return AHT21_ERRORRESOURCE;
}

/**
  * @brief  Function for reading AHT21 ID from outside calling.
	*
  * @param[in]   p_aht21_instance	: The pointer to object of bsp_aht21_driver_t
  * 
  * @return		ID of AHT21	: [uint8_t] ID if AHT21 Device.
	*
 **/
static uint8_t aht21_read_id(bsp_aht21_driver_t * const p_aht21_instance)
{
		if( !IS_INITED)
		{
				return AHT21_ERRORRESOURCE;
		}
		log_d("aht21_read_id");
    return g_device_id;
}

/**
  * @brief	Function for reading AHT21 Driver Layer Init.
	*
	* @param[in]	aht21_instance	: The pointer to object of bsp_aht21_driver_t.
  * 
  * @return    aht21_status_t
	*
 **/
static aht21_status_t aht21_init(bsp_aht21_driver_t * const p_aht21_instance)
{		
		log_d("tim = %d",p_aht21_instance->p_timebase_instance->pf_get_tick_count());
	
		aht21_status_t ret = AHT21_OK;
		log_d("aht21_init start");
		p_aht21_instance -> p_yield_instance -> pf_rtos_yield(300);
		
	
		log_d("tim = %d",p_aht21_instance->p_timebase_instance->pf_get_tick_count());
    if(NULL == p_aht21_instance -> p_iic_driver_instance			||
				NULL == p_aht21_instance -> p_iic_driver_instance -> pf_iic_init)
		{
			log_e("p_iic_driver_instance is NULL");
		}
		
		// Enter Critical Segmnet

#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_enter(NULL);
#endif	/* End of HARDWARE_IIC*/
		
		//	Init the IIC Interface
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_init(NULL);
		
		log_d("aht21_init iic_driver_init----------");
		
		//	Read the ID internally
		ret = __read_id(p_aht21_instance);
		if( AHT21_OK != ret )
		{
				return AHT21_ERRORRESOURCE;
		}
		//Exit Critical Segmnet
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_exit(NULL);
#endif	/* End of HARDWARE_IIC*/
		
		// Change the Flag of the internal flag
		g_inited = 1;
		
		//Read the AHT21 ID
		log_d("aht21's ID = [0x%x]",aht21_read_id(p_aht21_instance));
		
    return AHT21_OK;
}

/**
  * @brief  Function for reading AHT21 Driver Layer Deinit.
	*
	* @param[in]	aht21_instance	:	The pointer to object of bsp_aht21_driver_t
  * 
  * @return		aht21_status_t
	*
 **/
static aht21_status_t aht21_deinit(bsp_aht21_driver_t * const p_aht21_instance)
{
		g_inited = AHT21_NOT_INITED;
    //	TBD	
    return AHT21_OK;
}

/**
  * @brief	Function for reading AHT21 Status.
	*
	* @param[in]	aht21_instance	: The pointer to object of bsp_aht21_driver_t
  * 
	* @return		aht21_status	:	[uint8_t] aht21 status.
 **/
static uint8_t aht21_read_status(bsp_aht21_driver_t * const p_aht21_instance)
{
    if( !IS_INITED )
		{
				return AHT21_ERRORRESOURCE;
		}
		uint8_t rx_data = 0;
		
		// Enter Critical Segmnet
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_enter(NULL);
#endif	/* End of HARDWARE_IIC*/
		
		// Send the IIC start Signal
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_start(NULL);
		
		// Send the address of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_bytes(NULL,\
																																			AHT21_REG_READ_ADDR);
		
		//	Wait the ACK of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_wait_ack(NULL);
		
		
		//	Receive the data from slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_receive_bytes(NULL,
																																		&rx_data);
		
		//	Send the non-ack signal from Master to Slave
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_no_ack(NULL);
		
		//	Send the stop signal
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_stop(NULL);
		
		//	Exit Critical Segmnet
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_exit(NULL);
#endif	/* End of HARDWARE_IIC*/

		log_d("rx_data=%#x", rx_data);
		
    return rx_data;  
}

/**
  * @brief	Function for reading AHT21 tempraturn and humidity.
	*
	* @param[in]	aht21_instance	: The pointer to object of bsp_aht21_driver_t
	* @param[in]	temp	: [float *] The pointer to reference of temp variable.
	* @param[in]	humi	: [float *] The pointer to reference of humi variable.
	*	
	* @return		aht21_status	:	[uint8_t] aht21 status.
	*
 **/
static aht21_status_t aht21_read_temp_humi(\
																bsp_aht21_driver_t * const p_aht21_instance,
																float * const temp,
																float * const humi)
{
		if( !IS_INITED )
		{
				return AHT21_ERRORRESOURCE;
		}
		uint8_t			cnt = 5;
		uint8_t			byte_1th = 0;
		uint8_t			byte_2th = 0;
		uint8_t			byte_3th = 0;
		uint8_t			byte_4th = 0;
		uint8_t			byte_5th = 0;
		uint8_t			byte_6th = 0;
		uint32_t		retu_data = 0;

		//	Enter Critical Segmnet
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_enter(NULL);
#endif	/* End of HARDWARE_IIC*/
		
		// Send the IIC start Signal
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_start(NULL);
		
		// Send the address of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_bytes(NULL,\
																														AHT21_REG_WRITE_ADDR);
		
		// Send the response of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_wait_ack(NULL);
		
		// Send command to ask aht21 prepare data
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_bytes(NULL,\
																										AHT21_REG_MEASURE_CMD);
		
		//	Wait the ACK of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_wait_ack(NULL);
		
		// Send command to configure aht21 parameter[1]
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_bytes(NULL,\
																										AHT21_REG_MEASURE_CMD_ARGS1);
		
		//	Wait the ACK of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_wait_ack(NULL);

		// Send command to configure aht21 parameter[2]
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_bytes(NULL,\
																										AHT21_REG_MEASURE_CMD_ARGS2);
		
		//	Wait the ACK of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_wait_ack(NULL);
		
		//	Send the stop signal
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_stop(NULL);
		
		//	Exit Critical Segmnet
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_exit(NULL);
#endif	/* End of HARDWARE_IIC*/
		
		// Do the delay with AHT21_MEASURE_WAITING_TIME
		int32_t start_time = \
								p_aht21_instance -> p_timebase_instance -> pf_get_tick_count();
		
		while(p_aht21_instance -> p_timebase_instance -> pf_get_tick_count() - 
																				start_time < AHT21_MEASURE_WAITING_TIME)
		{
#ifdef OS_SUPPORTING
				p_aht21_instance -> p_yield_instance ->pf_rtos_yield(AHT21_MEASURE_WAITING_TIME);
#endif	/* End of OS_SUPPORTING*/
		}
		
		// Do the delay with AHT21_MEASURE_WAITING_TIME
		while((0x80 == (aht21_read_status(p_aht21_instance) &0x80)) && cnt)
		{
				p_aht21_instance -> p_yield_instance ->pf_rtos_yield(5);
				cnt--;
				if( 0 == cnt)
				{
						return AHT21_ERRORTIMEOUT;
				}
		}
		
		log_d("read temp");
		
		//	Enter Critical Segmnet
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_enter(NULL);
#endif	/* End of HARDWARE_IIC*/
		
		// Send the IIC start Signal
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_start(NULL);
		
		// Send the address of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_bytes(NULL,\
																														AHT21_REG_READ_ADDR);
		
		// Send the response of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_wait_ack(NULL);
		
		// Receive the data from slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_receive_bytes(NULL,\
																																			&byte_1th);
		
		//	Wait the ACK of IIC slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_ack(NULL);
		
		// Receive the data from slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_receive_bytes(NULL,\
																																			&byte_2th);
		// Send the ACK signal from Master to slave 
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_ack(NULL);
		
		// Receive the data from slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_receive_bytes(NULL,\
																																			&byte_3th);
		
		// Send the ACK signal from Master to slave 
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_ack(NULL);
		
		// Receive the data from slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_receive_bytes(NULL,\
																																			&byte_4th);
		
		// Send the ACK signal from Master to slave 
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_ack(NULL);
		
		// Receive the data from slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_receive_bytes(NULL,\
																																			&byte_5th);
		
		// Send the ACK signal from Master to slave 
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_ack(NULL);
		
		// Receive the data from slave device
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_receive_bytes(NULL,\
																																			&byte_6th);
		
		//	Send the non-ack signal from Master to Slave
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_send_no_ack(NULL);
		
		//	Send the stop signal
		p_aht21_instance -> p_iic_driver_instance -> pf_iic_stop(NULL);
		
		//	Exit Critical Segmnet
#ifndef HARDWARE_IIC
		p_aht21_instance -> p_iic_driver_instance -> pf_critical_exit(NULL);
#endif	/* End of HARDWARE_IIC*/

		retu_data = (retu_data|byte_2th)<<8;
		retu_data = (retu_data|byte_3th)<<8;
		retu_data = (retu_data|byte_4th);
		retu_data = retu_data >> 4;
		
		*humi = (retu_data * 1000 >> 20);
		*humi /= 10;
		
		retu_data = 0;
		retu_data = (retu_data|(byte_4th&0x0f))<<8;
		retu_data = (retu_data|byte_5th)<<8;
		retu_data = (retu_data|byte_6th);
		retu_data = retu_data&0xfffff;
		*temp = ((retu_data * 2000 >> 20) - 500);
		*temp /= 10;
		return AHT21_OK;
}

/**
  * @brief	Function for reading AHT21 humidity.
	*
	* @param[in]	aht21_instance	: The pointer to object of bsp_aht21_driver_t
	* @param[out]	humi	: [float *] The pointer to reference of humi variable.
	*	
	* @return		aht21_status	:	[uint8_t] aht21 status.
	*
 **/
static aht21_status_t aht21_read_humidity(\
																bsp_aht21_driver_t * const p_aht21_instance,
																													float * const humi)
{
		if( !IS_INITED)
		{
				return AHT21_ERRORRESOURCE;
		}
    return AHT21_OK;
}

/**
  * @brief	Function for make AHT21 sleep.
	*
	* @param[in]	aht21_instance	: The pointer to object of bsp_aht21_driver_t
	*	
	* @return		aht21_status	:	[uint8_t] aht21 status.
	*
 **/
static aht21_status_t aht21_sleep(bsp_aht21_driver_t * const p_aht21_instance)
{
		if( !IS_INITED)
		{
				return AHT21_ERRORRESOURCE;
		}
    return AHT21_OK;
}

/**
  * @brief	Function for make AHT21 wake-up.
	*
	* @param[in]	aht21_instance	: The pointer to object of bsp_aht21_driver_t
	*	
	* @return		aht21_status	:	[uint8_t] aht21 status.
	*
 **/
static aht21_status_t aht21_wakeup(bsp_aht21_driver_t * const p_aht21_instance)
{
		if( !IS_INITED)
		{
				return AHT21_ERRORRESOURCE;
		}
    return AHT21_OK;
}

/**
  * @brief	Function for instantiate AHT21 object.
	*
	* @param[in]	p_aht21_instance	: The pointer to object of bsp_aht21_driver_t
	*	@param[in]	p_iic_driver_instance	:	The pointer to reference of \
																												iic_driver_interface_t.
	*	@param[in]	p_timebase_instance	:	The pointer to reference of \
																												timebase_interface_t.
	*	@param[in]	p_tield_instance	:	The pointer to reference of \
																												yield_interface_t.
	*
	* @return		aht21_status	:	[uint8_t] aht21 status.
	*
 **/
aht21_status_t aht21_inst(
												bsp_aht21_driver_t * const p_aht21_instance,
												iic_driver_interface_t * const p_iic_driver_instance,
#ifdef OS_SUPPORTING
												yield_interface_t * const p_yield_instance,
#endif	//End of OS_SUPPORTING
												timebase_interface_t * const p_timebase_instance
													)
{
		if( IS_INITED)
		{
				return AHT21_ERRORRESOURCE;
		}
		
		log_d("aht21_inst start");
		uint8_t ret = 0;
		if(NULL == p_aht21_instance || NULL == p_iic_driver_instance)
		{
				return AHT21_ERROR;
		}
		
		p_aht21_instance -> p_iic_driver_instance = p_iic_driver_instance;
		p_aht21_instance -> p_timebase_instance = p_timebase_instance;
		p_aht21_instance -> p_yield_instance = p_yield_instance;
		
		p_aht21_instance -> pf_init = (aht21_status_t (*)(void * const))\
																													aht21_init;
		p_aht21_instance -> pf_deinit = (aht21_status_t (*)(void * const))\
																													aht21_deinit;
		p_aht21_instance -> pf_read_id = (aht21_status_t (*)(void * const))\
																													aht21_read_id;
		p_aht21_instance -> pf_read_temp_humi = \
							(aht21_status_t (*)(void * const,float * const temp,
																								float * const humi))\
																													aht21_read_temp_humi;
		
		p_aht21_instance -> pf_read_humi = \
							(aht21_status_t (*)(void * const,float * const humi))\
																								aht21_read_humidity;
		
		p_aht21_instance -> pf_sleep = (aht21_status_t (*)(void * const))\
																			aht21_sleep;
		
		p_aht21_instance -> pf_wakeup = (aht21_status_t (*)(void * const))\
																			aht21_wakeup;
		
		/* call the init function */
		ret = aht21_init(p_aht21_instance);
		log_d("aht21_init ret = %d",ret);
		if(ret)
		{
				log_e("aht21_init failed");
				return AHT21_ERROR;
		}
		log_d("aht21_init end");
		return AHT21_OK;
}


//******************************** Functions ********************************//

